/*
 * Created by SharpDevelop.
 */
using System;
using System.Collections.Generic;

namespace MiniMIPS
{
	/// <summary>
	/// Description of 
	/// </summary>
	/// 
	
	public enum OpType { RType, IType, JType };
	public enum OpFunc { Add, Sub, And, Or, Load, Store, BranchEq, Jump };
	public enum Op { ADD, SUB, AND, OR, ADDI, LW, SW, BEQ, J};
	
	
	public class OperationList : List<Operation>
	{		
		public OperationList()
		{
			base.Add(new Operation("add",  OpType.RType, OpFunc.Add));
			base.Add(new Operation("sub",  OpType.RType, OpFunc.Sub));
			base.Add(new Operation("and",  OpType.RType, OpFunc.And));
			base.Add(new Operation("or",   OpType.RType, OpFunc.Or));
			base.Add(new Operation("addi", OpType.IType, OpFunc.Add));
			base.Add(new Operation("lw",   OpType.IType, OpFunc.Load));
			base.Add(new Operation("sw",   OpType.IType, OpFunc.Store));
			base.Add(new Operation("beq",  OpType.IType, OpFunc.BranchEq));
			base.Add(new Operation("j",    OpType.JType, OpFunc.Jump));
		}
		
	}
	
	[Serializable]
	public class Operation
	{
		public string Name;

		public OpType Type;
		public OpFunc Func;
		
		public uint OpCode
		{
			get
			{
				uint code;
				
				if(Type == OpType.RType)
				{
					code = 0; // all RType have 0000 OpCode
				}
				else if(Type == OpType.IType)
				{
					switch (Func) {
						case OpFunc.Add:
							code = 4;
							break;
						case OpFunc.Load:
							code = 11;
							break;
						case OpFunc.Store:
							code = 15;
							break;
						case OpFunc.BranchEq:
							code = 8;
							break;
						default:
							throw new Exception("Unrecognised IType Function");
					}
				}
				else if(Type == OpType.JType)
				{
					code = 2;
				}
				else
				{
					throw new Exception("Unrecognised Operation Type and Function");
				}
				
				return code;
			}
		}
		
		
		public uint FuncCode
		{
			get
			{
				uint code;
				
				if(Type == OpType.RType)
				{
					switch (Func) {
						case OpFunc.Add:
							code = 0;
							break;
						case OpFunc.Sub:
							code = 2;
							break;
						case OpFunc.And:
							code = 4;
							break;
						case OpFunc.Or:
							code = 5;
							break;
						default:
							throw new Exception("Unrecognised FunCode Function");
					}	
				}
				else
				{
					throw new Exception("Non-RType Operations Have No FuncCode");
				}
				
				return code;
			}
		}
		
		public override bool Equals(object obj)
		{
			if(obj is Operation)
			{
				Operation op = (Operation)obj;
				
				return (this.Func.Equals(op.Func) &&
				        this.Name.Equals(op.Name) &&
				        this.Type.Equals(op.Type));
			}
			else
			{
				return false;
			}
		}
		
		public override int GetHashCode()
		{
			return this.Func.GetHashCode() + this.Type.GetHashCode() + this.Name.GetHashCode();
		}
		
		public Operation()
		{
			Name = "ADD";
			Type = OpType.RType;
			Func = OpFunc.Add;
		}
			
		public Operation(string name, OpType type, OpFunc func)
		{
			Name = name;
			Type = type;
			Func = func;
		}
		
		public override string ToString()
		{
			return Name;
		}
	}
	
}
